package persistence ;

import java.util.Arrays;
import java.util.Collection;

import junit.framework.Assert;
import junit.framework.TestCase;
import util.NotFoundException;

/**
 * @author idan
 */
public abstract class PersistenceControllerTest extends TestCase
{
	private PersistenceController persistence ;
	private Dummy[] dummies ;

	/**
	 * Test method for {@link persistence.PersistenceController#delete(long)}.
	 */
	public void testDelete()
	{
		try
		{
			this.persistence.startTransaction() ;
			for (int i = 0 ; i < this.dummies.length ; i += 2)
			{
				this.persistence.delete(this.dummies[i].getId()) ;
			}
			this.persistence.commitTransaction() ;
		}
		catch (NotFoundException e)
		{
			Assert.fail("objects were not stored in the database") ;
		}
		catch (Exception e)
		{
			e.printStackTrace() ;
		}
		finally
		{
			try
			{
				this.persistence.commitTransaction() ;
			}
			catch (RuntimeException e)
			{
				// nothing to commit - ok
			}
		}

		try
		{
			for (int i = 1 ; i < this.dummies.length ; i += 2)
			{
				this.persistence.startTransaction() ;
				Persistent p = this.persistence.get(this.dummies[i].getId()) ;
				this.persistence.commitTransaction() ;
				Assert.assertNotNull(p) ;
				Assert.assertEquals(this.dummies[i], p) ;
			}
		}
		finally
		{
			try
			{
				this.persistence.commitTransaction() ;
			}
			catch (RuntimeException e)
			{
				// nothing to commit - ok
			}
		}

		for (int i = 0 ; i < this.dummies.length ; i += 2)
		{
			try
			{
				this.persistence.startTransaction() ;
				Persistent p = this.persistence.get(this.dummies[i].getId()) ;
				Assert.assertNull(p) ;
			}
			finally
			{
				try
				{
					this.persistence.commitTransaction() ;
				}
				catch (RuntimeException e)
				{
					// nothing to commit - ok
				}
			}
		}

		try
		{
			this.persistence.delete(-5) ;
			Assert.fail("exception not thrown") ;
		}
		catch (NotFoundException e)
		{
			// pass
		}
	}

	/**
	 * Test method for {@link persistence.PersistenceController#deleteAll()}.
	 */
	@SuppressWarnings("unchecked")
	public void testDeleteAll()
	{
		this.persistence.startTransaction() ;
		this.persistence.deleteAll() ;
		this.persistence.commitTransaction() ;
		Collection<Dummy> all = (Collection<Dummy>)this.persistence.getAll();
		Assert.assertTrue(all.isEmpty()) ;
	}

	/**
	 * Test method for {@link persistence.PersistenceController#get(long)}.
	 */
	public void testGet()
	{
		try
		{
			for (int i = 0 ; i < this.dummies.length ; i += 2)
			{
				this.persistence.startTransaction() ;
				Dummy dummy = (Dummy)this.persistence.get(this.dummies[i].getId()) ;
				this.persistence.commitTransaction() ;
				Assert.assertNotNull(dummy) ;
				Assert.assertEquals(this.dummies[i].getId(), dummy.getId()) ;
			}
		}
		finally
		{
			try
			{
				this.persistence.commitTransaction() ;
			}
			catch (RuntimeException e)
			{
				// nothing to commit - ok
			}
		}
	}

	/**
	 * Test method for {@link persistence.PersistenceController#getAll()}.
	 */
	@SuppressWarnings("unchecked")
	public void testGetAll()
	{
		this.persistence.startTransaction() ;
		Collection<Dummy> all = (Collection<Dummy>)this.persistence.getAll();
		this.persistence.commitTransaction() ;
		for (Dummy dummy: all)
		{
			Assert.assertTrue(this.collectionContainsId(Arrays.asList(this.dummies), dummy.getId())) ;
		}
		for (Dummy dummy: this.dummies)
		{
			Assert.assertTrue(this.collectionContainsId(all, dummy.getId())) ;
		}
	}

	/**
	 * @param load whether to load or start a new database.
	 * @return the an instance of a peristence controller.
	 * @throws CorruptDataException if the database was corrupt
	 */
	protected abstract PersistenceController getNewPersistence(boolean load) throws CorruptDataException ;

	/**
	 * @see junit.framework.TestCase#setUp()
	 */
	@Override
	protected void setUp() throws Exception
	{
		super.setUp() ;
		this.persistence = this.getNewPersistence(PersistenceController.BUILD_NEW) ;
		this.dummies = new Dummy[20] ;
		this.setupDatabase() ;
		this.persistence.close() ;
		this.persistence = this.getNewPersistence(PersistenceController.LOAD) ;
	}

	/**
	 * @see junit.framework.TestCase#tearDown()
	 */
	@Override
	protected void tearDown() throws Exception
	{
		this.persistence.deleteAll() ;
		this.persistence.close() ;
		super.tearDown() ;
	}

	private boolean collectionContainsId(final Collection<Dummy> collection, final long id)
	{
		for (Dummy d: collection)
		{
			if (d.getId() == id)
			{
				return true ;
			}
		}
		return false ;
	}

	private void setupDatabase()
	{
		this.persistence.startTransaction() ;
		for (int i = 0 ; i < this.dummies.length ; i++)
		{
			this.dummies[i] = new Dummy("dummy " + i) ;
			this.persistence.put(this.dummies[i]) ;
		}
		this.persistence.commitTransaction() ;
	}

}
